﻿using System;
using System.Collections.Generic;
using NUnit.Framework;
using Rhino.Mocks;
using SimpleServiceBus.Messages.Management.Monitoring;
using SimpleServiceBus.Persistence.Domain.Management.Entities;
using SimpleServiceBus.Persistence.NHibernate;
using SimpleServiceBus.Persistence.NHibernate.Repositories;

namespace SimpleServiceBus.Tests.Persistence.NHibernate
{
    [TestFixture]
    public class EndpointStatusRepositoryTestFixture
    {
        //[TestFixtureSetUp]
        //public void FixtureSetUp()
        //{
        //    DataSessionManager.Create<ThreadStaticSession>();
        //    ((DataSessionManager<ThreadStaticSession>)DataSessionManager.Current).CreateSchema();
        //}

        //[TestFixtureTearDown]
        //public void FixtureTearDown()
        //{
        //    if (DataSessionManager.Current != null)
        //        DataSessionManager.Current.Dispose();
        //}

        [SetUp]
        public void TestSetUp()
        {
            DataSessionManager.Create<ThreadStaticSession>();
        }

        //[TearDown]
        //public void TearDown()
        //{
        //    if (DataSessionManager.Current.CurrentSession != null)
        //        DataSessionManager.Current.CurrentSession.Flush();
        //}

        [Test]//Tests need embellishment
        public void UpdateEndpointStatusShouldCreteEndpointAndAddNewMeasurements()
        {
            //Given
            var endpointRepo = MockRepository.GenerateStub<IRepository<ManagedEndpoint>>();
            var metricRepo = MockRepository.GenerateStub<IRepository<EndpointMetricType>>();
            var perfRepo = MockRepository.GenerateStub<IRepository<EndpointPerformanceMeasurement>>();

            endpointRepo.Stub(er => er.FindOne(null)).IgnoreArguments().Return(null);
            metricRepo.Stub(mr => mr.FindOne(null)).IgnoreArguments().Return(null);
            

            var update = new EndpointStatusUpdateNotification
                             {
                                 EndpointID = "EPID",
                                 StatusTime = DateTime.Today,
                                 EndpointMeasurements = new List<EndpointMeasurement>
                                                            {
                                                                new EndpointMeasurement
                                                                    {
                                                                        CanAggregate = true,
                                                                        DataType = MeasurementDataType.Numeric,
                                                                        Timestamp = DateTime.Today.AddHours(1),
                                                                        Name = "A Metric",
                                                                        UnitOfMeasurement = "Degrees of Despair",
                                                                        Value = "100.001"
                                                                    }
                                                            }
                             };
            var statusRepo = new EndpointStatusRepository(endpointRepo, metricRepo, perfRepo, null, null)
                                 {
                                     PersistIncrementalStatusUpdates = true
                                 };
            
            //When
            statusRepo.AddEndpointStatus(update);

            //Then
            endpointRepo.AssertWasCalled(
                er => er.SaveOrUpdate(Arg<ManagedEndpoint>.Matches(ep => ep.EndpointID == "EPID")));
            metricRepo.AssertWasCalled(
                er => er.SaveOrUpdate(Arg<EndpointMetricType>.Matches(emt => emt.Name == "A Metric")));
            perfRepo.AssertWasCalled(pr=>pr.SaveOrUpdate(Arg<EndpointPerformanceMeasurement>.Matches(epm => epm.NumericValue == 100.001)));
        }

        [Test]
        public void AddEndpointErrorShouldPersistErrorMessag()
        {
            //Given
            var endpointRepo = MockRepository.GenerateStub<IRepository<ManagedEndpoint>>();
            var errorRepo = MockRepository.GenerateStub<IRepository<EndpointError>>();

            var error = new EndpointErrorNotification
                            {
                                EndpointID = "EPID",
                                ErrorMessage = "Uh Oh...",
                                StatusTime = DateTime.Today
                            };
            var statusRepository = new EndpointStatusRepository(endpointRepo, null, null, null, errorRepo);

            //When
            statusRepository.AddEndpointError(error);

            //Then
            errorRepo.AssertWasCalled(er=>er.SaveOrUpdate(Arg<EndpointError>.Matches(e=>e.ErrorMessage=="Uh Oh...")));
        }

        [Test]
        public void UpdateEndpointOnlineStatusShouldMarkAnEndpointAsBeingOnline()
        {
            //given
            var endpointRepo = MockRepository.GenerateStub<IRepository<ManagedEndpoint>>();

            var ep = new ManagedEndpoint {EndpointID = "AnEndpoint", Status = EndpointStatus.Offline};
            endpointRepo.Stub(er => er.FindOne(null)).IgnoreArguments().Return(ep);

            var notification = new EndpointOnlineNotification{EndpointID = "AnEndpoint",StatusTime = DateTime.Today};

            var statusRepository = new EndpointStatusRepository(endpointRepo, null, null, null,null);
            
            //When
            statusRepository.UpdateEndpointOnlineStatus(notification);

            //Then
            endpointRepo.AssertWasCalled(
                er => er.SaveOrUpdate(Arg<ManagedEndpoint>.Matches(e => e.Status == EndpointStatus.Online)));
        }

        [Test]
        public void UpdateEndpointOfflineStatusShouldMarkAnEndpointAsBeingOffline()
        {
            //given
            var endpointRepo = MockRepository.GenerateStub<IRepository<ManagedEndpoint>>();

            var ep = new ManagedEndpoint { EndpointID = "AnEndpoint", Status = EndpointStatus.Offline };
            endpointRepo.Stub(er => er.FindOne(null)).IgnoreArguments().Return(ep);

            var notification = new EndpointOfflineNotification
                                   {
                                       EndpointID = "AnEndpoint",
                                       StatusTime = DateTime.Today,
                                       EstimatedOutageDurationInMinutes = 10,
                                       OfflineReason = "Is Broken"
                                   };

            var statusRepository = new EndpointStatusRepository(endpointRepo, null, null, null, null);

            //When
            statusRepository.UpdateEndpointOfflineStatus(notification);

            //Then
            endpointRepo.AssertWasCalled(
                er => er.SaveOrUpdate(
                    Arg<ManagedEndpoint>.Matches(
                        e => e.Status == EndpointStatus.Offline &&
                        e.LastStatusUpdate == DateTime.Today &&
                        e.StatusNote == "Is Broken"
                        )));
        }

        [Test]
        public void UpdateEndpointExpiredStatusShouldMarkAnEndpointAsBeingUnknown()
        {
            //given
            var endpointRepo = MockRepository.GenerateStub<IRepository<ManagedEndpoint>>();

            var ep = new ManagedEndpoint { EndpointID = "AnEndpoint", Status = EndpointStatus.Offline };
            endpointRepo.Stub(er => er.FindOne(null)).IgnoreArguments().Return(ep);

            var notification = new EndpointStatusExpiredNotification
                                   {
                                       EndpointID = "AnEndpoint",
                                       StatusTime = DateTime.Today.AddDays(1),
                                       LastStatusReceived = DateTime.Today
            };

            var statusRepository = new EndpointStatusRepository(endpointRepo, null, null, null, null);

            //When
            statusRepository.UpdateEndpointExpiredStatus(notification);

            //Then
            endpointRepo.AssertWasCalled(
                er => er.SaveOrUpdate(
                    Arg<ManagedEndpoint>.Matches(
                        e => e.Status == EndpointStatus.Unknown &&
                        e.LastStatusUpdate == DateTime.Today &&
                        e.StatusNote == "An expected heartbeat for this endpoint has not been received. This endpoint last reported its status @ " + notification.LastStatusReceived
                        )));
        }

        [Test]
        public void UpdateStatusSummariesShouldCreateAllIncludedSummaries()
        {
            //given
            var endpointRepo = MockRepository.GenerateStub<IRepository<ManagedEndpoint>>();
            var summaryRepo = MockRepository.GenerateStub<IRepository<EndpointPerformanceSummary>>();
            var metricRepo = MockRepository.GenerateStub<IRepository<EndpointMetricType>>();


            var ep = new ManagedEndpoint { EndpointID = "AnEndpoint", Status = EndpointStatus.Offline };
            endpointRepo.Stub(er => er.FindOne(null)).IgnoreArguments().Return(ep);

            var metric = new EndpointMetricType {Name = "A Metric"};
            metricRepo.Stub(mr => mr.FindOne(null)).IgnoreArguments().Return(metric);

            var update = new EndpointStatusSummaryNotification
                             {
                                 AggregationStartTime = DateTime.Today.AddHours(-1),
                                 AggregationStopTime = DateTime.Today,
                                 ActiveEndpoints = new List<EndpointStatusSummary>
                                   {
                                       new EndpointStatusSummary
                                           {
                                               EndpointID = "EP1",
                                               StatusTime = DateTime.Today,
                                               StatusMeasurements = new List<EndpointMeasurementSummary>
                                                {
                                                    new EndpointMeasurementSummary
                                                        {
                                                            LastMeasurement = new EndpointMeasurement
                                                                                  {
                                                                                      Name = "A Metric"
                                                                                  },
                                                            Average = 10.0,
                                                            Max = 9.0,
                                                            Min = 1.0,
                                                            SampleCount = 100,
                                                            Sum = 2.0
                                                        }
                                                }
                                           }
                                   }
                             };

            var statusRepository = new EndpointStatusRepository(endpointRepo, metricRepo, null, summaryRepo,null);

            //When
            statusRepository.UpdateStatusSummaries(update);

            //Then
           
            summaryRepo.AssertWasCalled(sr=>sr.SaveOrUpdate(Arg<EndpointPerformanceSummary>.Matches
                (s=>
                    s.SampleCount == 100 &&
                    s.Metric.Name == metric.Name &&
                    s.Average == 10.0 && 
                    s.Max == 9.0 && 
                    s.Min == 1.0 &&
                    s.Sum == 2.0 && 
                    s.ManagedEndpoint.EndpointID == ep.EndpointID
                 )));

        }


    }
}
